package io.process.plus;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import io.process.plus.output.OutputParser;
import io.process.plus.output.OutputParserJob;

/**
 * 升级版命令行执行方法，支持同步执行、输出读取、超时取消、主动取消等，适用于Windows、Linux、Mac平台
 * 
 * @author baochunliang
 * @date 2015/07/04
 */
public class ProcessPlus {

    /**
     * 执行命令
     * 
     * @param parser 输出解析器
     * @param option 常用参数
     * @param command 命令
     * @return 命令执行成功/失败
     */
    public static boolean exec(OutputParser parser, ProcessOption option, String... command) {
        List<String> list = new ArrayList<String>(command.length);
        for (String arg : command) {
            list.add(arg);
        }
        return exec(parser, option, list);
    }

    /**
     * 执行命令
     * 
     * @param parser 输出解析器
     * @param option 常用参数
     * @param list 命令
     * @return 命令执行成功/失败
     */
    public static boolean exec(OutputParser parser, ProcessOption option, List<String> list) {
        boolean ret = false;

        // 参数获取
        if (option == null) {
            option = new ProcessOption();
        }
        String workingDirectory = option.getWorkingDirectory();
        long timeoutInSec = option.getTimeoutInSec();
        // 超时时间不能小于1秒
        if (timeoutInSec < 1) {
            timeoutInSec = Long.MAX_VALUE;
        }
        String timeoutExceptionMessage = option.getTimeoutExceptionMessage();
        String charsetName = option.getCharsetName();

        ProcessBuilder pb = new ProcessBuilder(list);
        pb.redirectErrorStream(true);

        // 设置命令执行目录
        if (workingDirectory != null) {
            pb.directory(new File(workingDirectory));
        }

        OutputParserJob job = null;
        try {
            final Process process = pb.start();
            job = new OutputParserJob(parser, process, charsetName);
            job.start();

            // 执行命令
            Callable<Integer> processCall = new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    process.waitFor();
                    return process.exitValue();
                }
            };
            ExecutorService service = Executors.newSingleThreadExecutor();
            Future<Integer> processFuture = service.submit(processCall);
            service.shutdown();

            // 执行结果
            try {
                int exitValue = processFuture.get(timeoutInSec > 0 ? timeoutInSec : Long.MAX_VALUE, TimeUnit.SECONDS);
                System.out.println("命令执行结果：" + exitValue);
                if (exitValue == 0 || job.isJobCanceled()) {
                    ret = true;
                }
            } catch (TimeoutException e) {
                // 超时处理
                String message = "命令执行超时，进程关闭，当前命令：" + Arrays.toString(list.toArray());
                if (timeoutExceptionMessage != null) {
                    message = timeoutExceptionMessage;
                }
                System.out.println(message);
                process.destroy();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (job != null) {
                job.interrupt();
            }
        }
        return ret;
    }
}